﻿#include "epoller.h"
#include "log.h"
#include "channel.h"
#include <cstring>


Epoller::Epoller()
{
    // 创建epoll实例
    m_epfd = epoll_create(MAX_EPOLL_EVENTS);
    if(m_epfd < 0)
    {
        LOG(Error, "epoll_create failed");
        abort();
    }
}

Epoller::~Epoller()
{
    
}

// 判断channel是否存在
bool Epoller::isHasChannel(Channel *channel)
{
    return m_channels.find(channel->getFd()) != m_channels.end();
}

// 更新事件监控
void Epoller::update(Channel *channel, int op)
{
    int fd = channel->getFd();
    struct epoll_event ev;
    ev.events = channel->getEvents();
    ev.data.fd = fd;
    int ret = epoll_ctl(m_epfd, op, fd, &ev);
    if(ret < 0)
    {
        LOG(Error, "epoll_ctl failed, fd: %d, op: %d, errno: %d, %s", fd, op, errno, strerror(errno));
    }
}

// 更新事件
void Epoller::updateEvent(Channel *channel)
{
    // 如果channel存在，则修改，否则添加
    if(isHasChannel(channel))
    {
        update(channel, EPOLL_CTL_MOD);
    }
    else
    {
        m_channels.insert({channel->getFd(), channel});
        update(channel, EPOLL_CTL_ADD);
    }
}

// 等待事件
void Epoller::waitEvent(std::vector<Channel *> &actives)
{
    // 1. 调用epoll_wait等待事件
    int nfds = epoll_wait(m_epfd, m_events, MAX_EPOLL_EVENTS, -1);
    if(nfds < 0)
    {
        if(errno == EINTR)
        {
            return;
        }
        LOG(Fatal, "epoll_wait failed, nfds: %d, errno: %d, %s", nfds, errno, strerror(errno));
        abort();
    }
    // 2. 遍历事件列表，获取就绪事件
    for(int i = 0; i < nfds; i++)
    {
       auto it = m_channels.find(m_events[i].data.fd);
       if(it == m_channels.end())
       {
            // NOTE: 事件是一定存在，如果不存在则有严重的逻辑错误
            LOG(Fatal, "epoll_wait failed");
            abort();
       }
       Channel *channel = it->second;
       // 设置就绪事件
       channel->setRevents(m_events[i].events);
       // 添加到活跃列表
       actives.push_back(channel);
    }
}

// 删除事件
void Epoller::removeEvent(Channel *channel)
{
    auto it = m_channels.find(channel->getFd());
    if(it != m_channels.end())
    {
        m_channels.erase(it);
        update(channel, EPOLL_CTL_DEL);
    }
}
